package com.mango.iot.gateway.coap.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * <pre>
 * TODO
 * </pre>
 *
 * @Author xinguang.huang
 * @Date 2021/4/23
 **/
@Slf4j
public class SignUtil {

    /**
     * <pre>
     * 验证签名
     * </pre>
     *
     * @param params       参数
     * @param deviceSecret 设备密钥
     * @return java.lang.Boolean
     */
    public static Boolean verifySign(Map<String, Object> params, String deviceSecret) {
        return params.get("sign") != null && params.get("sign").toString().equals(sign(params, deviceSecret));
    }

    /**
     * 设备端签名。
     *
     * @param params，签名参数。
     * @param deviceSecret，设备密钥。
     * @return 签名十六进制字符串。
     */
    public static String sign(Map<String, Object> params, String deviceSecret) {

        // 请求参数按字典顺序排序。
        Set<String> keys = getSortedKeys(params);

        // sign、signmethod
        keys.remove("sign");
        keys.remove("signmethod");

        // 组装签名明文。
        StringBuffer content = new StringBuffer();
        for (String key : keys) {
            content.append(key);
            content.append(params.get(key));
        }

        // 计算签名。
        String sign = encrypt(content.toString(), deviceSecret, params.get("signmethod").toString());

        return sign;
    }

    /**
     * 获取JSON对象排序后的key集合。
     *
     * @param json，需要排序的JSON对象。
     * @return 排序后的key集合。
     */
    private static Set<String> getSortedKeys(Map<String, Object> json) {
        SortedMap<String, Object> map = new TreeMap<>();
        for (String key : json.keySet()) {
            map.put(key, json.get(key));
        }
        return map.keySet();
    }

    /**
     * 使用HMAC_ALGORITHM加密。
     *
     * @param content，明文。
     * @param secret，密钥。
     * @param signmethod，加密方式。
     * @return 密文。
     */
    private static String encrypt(String content, String secret, String signmethod) {
        try {
            byte[] text = content.getBytes(StandardCharsets.UTF_8);
            byte[] key = secret.getBytes(StandardCharsets.UTF_8);
            SecretKeySpec secretKey = new SecretKeySpec(key, signmethod);
            Mac mac = Mac.getInstance(secretKey.getAlgorithm());
            mac.init(secretKey);
            return Hex.encodeHexString(mac.doFinal(text));
        } catch (Exception e) {
            log.error("encrypt error={}", e);
            return null;
        }
    }
}
